/*
 *  This file is part of Cube.
 *
 *  Cube is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Cube is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Cube.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package cube;

import cube.data.DataManager;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.TooManyListenersException;
import javax.comm.CommPortIdentifier;
import javax.comm.PortInUseException;
import javax.comm.SerialPort;
import javax.comm.SerialPortEvent;
import javax.comm.SerialPortEventListener;
import javax.comm.UnsupportedCommOperationException;
import org.apache.log4j.Logger;

/**
 *
 * @author akermanl
 */
public class SerialConnection {
    
    protected static final Logger log = Logger.getLogger(CubeView.class);
    
    private String port;
    private int baudrate;
    private int stopBits;
    private int dataBits;
    private int parity;
    private boolean open;
    private SerialPort serialPort;
    private OutputStream outputStream;
    private InputStream inputStream;
    private DataManager dataManager;

    public SerialConnection(DataManager dataManager, String port, int baudrate, double stopBits, int dataBits, String parity) {
        this.dataManager = dataManager;
        this.setPort(port);
        this.setBaudrate(baudrate);
        this.setStopBits(stopBits);
        this.setDataBits(dataBits);
        this.setParity(parity);
        this.open = false;
    }
    

    
    public void openPort(){      
        if (!isOpen()){
            log.debug("Open serial port");
            String portName = this.getPort();
            Boolean foundPort = false;
            CommPortIdentifier serialPortId = null;
            Enumeration enumComm;
            enumComm = CommPortIdentifier.getPortIdentifiers();
            while(enumComm.hasMoreElements()) {
		serialPortId = (CommPortIdentifier) enumComm.nextElement();
		if (portName.contentEquals(serialPortId.getName())) {
                    foundPort = true;
                    break;
		}
            }
            if (foundPort != true) {
		log.debug("Serial port not found: " + portName);
		return;
            }
            try {
		setSerialPort((SerialPort) serialPortId.open("open and send", 500));
            } catch (PortInUseException e) {
		log.debug("Port in use");
		return;
            }
            try {
		setOutputStream(getSerialPort().getOutputStream());
            } catch (IOException e) {
		log.debug("No access on OutputStream");
		return;
            }
            try {
		setInputStream(getSerialPort().getInputStream());
            } catch (IOException e) {
		log.debug("No access on InputStream");
		return;
            }
            try {
            	getSerialPort().addEventListener(new serialPortEventListener());
            } catch (TooManyListenersException e) {
            	log.debug("TooManyListenersException for serial port");
		return;
            }
            getSerialPort().notifyOnDataAvailable(true);
            try {
                getSerialPort().setSerialPortParams(baudrate, dataBits, stopBits, parity);
            } catch(UnsupportedCommOperationException e) {
		log.debug("Couldn't set the interface parameters");
		return;
            }	            
            setOpen(true);
		return;
        }
         else {
            log.debug("Serial port is already open");
		return;
        }
    }

    public SerialPort getSerialPort() {
        return serialPort;
    }

    public void setSerialPort(SerialPort serialPort) {
        this.serialPort = serialPort;
    }

    public OutputStream getOutputStream() {
        return outputStream;
    }

    public void setOutputStream(OutputStream outputStream) {
        this.outputStream = outputStream;
    }

    public InputStream getInputStream() {
        return inputStream;
    }

    public void setInputStream(InputStream inputStream) {
        this.inputStream = inputStream;
    }
    
    class serialPortEventListener implements SerialPortEventListener {
	public void serialEvent(SerialPortEvent event) {
            log.debug("serialPortEventlistener"+event.getEventType());
            switch (event.getEventType()) {
                case SerialPortEvent.DATA_AVAILABLE:
                    serialPortEventDataAvailable();
                    break;
		case SerialPortEvent.BI:
		case SerialPortEvent.CD:
		case SerialPortEvent.CTS:
		case SerialPortEvent.DSR:
		case SerialPortEvent.FE:
		case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
		case SerialPortEvent.PE:
		case SerialPortEvent.RI:
		default:
            }
	}
    }	
    
    private void serialPortEventDataAvailable() {
        int num_xlow,num_xhigh,num_ylow,num_yhigh,num_zlow,num_zhigh;
		try {
			num_xlow = inputStream.read();
			num_xhigh = inputStream.read();			
			num_ylow = inputStream.read();
			num_yhigh = inputStream.read();
			num_zlow = inputStream.read();
			num_zhigh = inputStream.read();
                        
                        dataManager.processAccelerationData(num_xlow, num_xhigh, num_ylow, num_yhigh, num_zlow, num_zhigh);
				
            } catch (IOException e) {
		log.debug("Error occurred while receiving data");
            }	
	}
    
    public void closePort(){
        if (isOpen()) {
            log.debug("Close serial port");
            getSerialPort().close();
            setOpen(false);
        } else {
            log.debug("Serial port is already closed");
            
        }  
    }
    
    public void send(String message) {   
        if (!isOpen()){
            openPort(); 
        } 
        if (isOpen()){
             try { 
                getOutputStream().write(message.getBytes());
             } catch (IOException e) {
                log.debug("Error occurred while sending");
             }
        }
    }

    
    public String getPort() {
        return port;
    }

    public void setPort(String port) {
        this.port = port;
        log.debug("Set port to " +port);
    }

    public int getBaudrate() {
        return baudrate;
    }

    public void setBaudrate(int baudrate) {
        this.baudrate = baudrate;
        log.debug("Set baudrate to " +baudrate);
    }

    public double getStopBits() {
        return stopBits;
    }

    public void setStopBits(double stopBits) {
       if (stopBits == 1) { this.stopBits = SerialPort.STOPBITS_1; }
       else if (stopBits == 1.5) { this.stopBits = SerialPort.STOPBITS_1_5; }
       else if (stopBits == 2) { this.stopBits = SerialPort.STOPBITS_2; }
       log.debug("Set stop bits to " +stopBits);
    }

    public int getDataBits() {
        return dataBits;
    }

    public void setDataBits(int dataBits) {
        switch (dataBits) {
            case 5 : this.dataBits = SerialPort.DATABITS_5; break;
            case 6 : this.dataBits = SerialPort.DATABITS_6; break;
            case 7 : this.dataBits = SerialPort.DATABITS_7; break;
            case 8 : this.dataBits = SerialPort.DATABITS_8; break;
        }
        log.debug("Set data bits to " +dataBits);
    }

    public int getParity() {
        return parity;
    }

    public void setParity(String parity) {
        if (parity.equals("none")) { this.parity = SerialPort.PARITY_NONE ; }
        else if (parity.equals("even")) { this.parity = SerialPort.PARITY_EVEN ; }
        else if (parity.equals("odd")) { this.parity = SerialPort.PARITY_EVEN ; }
        log.debug("Set parity to " +parity);
    }

    public boolean isOpen() {
        return open;
    }

    public void setOpen(boolean open) {
        this.open = open;
    }
}
